package com.postsuperman.proxypool.dao;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.mongodb.client.MongoClient;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import com.postsuperman.proxypool.pojo.HttpProxy;
import com.postsuperman.proxypool.pojo.dto.QueryDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author ： Lwl postserupman
 * @Description TODO
 * @Date 2019/2/27 12:06
 * @Version 1.0
 */
@Component
public class ProxyDAOImpl implements ProxyDAO {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Value("${proxy.score.init}")
    private double initScore = 5;
    @Value("${proxy.score.min}")
    private double minScore = 0;
    @Value("${proxy.score.max}")
    private double maxScore = 10;

    @Override
    public boolean exists(HttpProxy proxy) {
        Query query = new Query();
        query.addCriteria(Criteria.where(HttpProxy.IP).is(proxy.getIp()));
        query.addCriteria(Criteria.where(HttpProxy.PORT).is(proxy.getPort()));
        return mongoTemplate.exists(query, Constant.COL_NAME_PROXY);
    }

    @Override
    public boolean insert(HttpProxy proxy) {
        if (proxy != null && !exists(proxy)) {
            proxy.setScore(initScore);
            proxy.setCreateTime(System.currentTimeMillis());
            mongoTemplate.save(proxy, Constant.COL_NAME_PROXY);
            return StrUtil.isNotBlank(proxy.getId());
        }
        return true;
    }

    @Override
    public boolean insertBatch(List<HttpProxy> proxies) {
        mongoTemplate.insertAll(proxies);
        return true;
    }

    @Override
    public boolean updateById(String id) {
        HttpProxy proxy = mongoTemplate.findById(id, HttpProxy.class);
        if (proxy != null) {
            Double score = proxy.getScore();
            Query query = new Query(Criteria.where(HttpProxy.ID).is(id));
            Update update = new Update();
            update.set(HttpProxy.LASTUPDATE_TIME, System.currentTimeMillis());        //最近一次验证成功的时间
            if (score < maxScore) {
                update.set(HttpProxy.SCORT, score + 1);
            }
            UpdateResult writeResult = mongoTemplate.updateFirst(query, update, HttpProxy.class, Constant.COL_NAME_PROXY);
            return writeResult != null && writeResult.getMatchedCount() > 0;
        }
        return false;
    }

    @Override
    public boolean update(HttpProxy proxy) {
        if (proxy != null) {
            Double score = proxy.getScore();
            Query query = new Query(Criteria.where(HttpProxy.ID).is(proxy.getId()));
            Update update = new Update();
            update.set(HttpProxy.LASTUPDATE_TIME, System.currentTimeMillis());        //最近一次验证成功的时间
            update.set(HttpProxy.MS, proxy.getMs());
            if (score < maxScore) {
                update.set(HttpProxy.SCORT, score + 1);
            }
            UpdateResult writeResult = mongoTemplate.updateFirst(query, update, HttpProxy.class, Constant.COL_NAME_PROXY);
            return writeResult != null && writeResult.getMatchedCount() > 0;
        }
        return false;
    }

    @Override
    public boolean delete(HttpProxy proxy) {
        if (proxy != null) {
            Double score = proxy.getScore();
            Query query = new Query(Criteria.where(HttpProxy.ID).is(proxy.getId()));
            if (score == null || score <= minScore) {
                DeleteResult writeResult = mongoTemplate.remove(query, HttpProxy.class, Constant.COL_NAME_PROXY);
                return writeResult != null && writeResult.getDeletedCount() > 0;
            } else {
                //如果ip高可用，在某个时间节点已经停用，但score很高，导致获取高质量ip时无法
                proxy.setScore(score - 1);
                Update update = new Update();
                update.set(HttpProxy.LASTUPDATE_TIME, System.currentTimeMillis());        //最近一次验证成功的时间
                update.set(HttpProxy.SCORT, proxy.getScore());
                update.set(HttpProxy.MS, proxy.getMs());
                UpdateResult writeResult = mongoTemplate.updateFirst(query, update, HttpProxy.class, Constant.COL_NAME_PROXY);
                return writeResult != null && writeResult.getMatchedCount() > 0;
            }
        }
        return true;
    }

    @Override
    public boolean deleteById(String id) {
        Query query = new Query(Criteria.where(HttpProxy.ID).is(id));
        DeleteResult writeResult = mongoTemplate.remove(query, HttpProxy.class, Constant.COL_NAME_PROXY);
        return writeResult != null && writeResult.getDeletedCount() > 0;
    }

    @Override
    public void deleteAll() {
        mongoTemplate.dropCollection(Constant.COL_NAME_PROXY);
    }

    @Override
    public List<HttpProxy> findByCond(QueryDTO queryDTO, boolean isGetAll) {
        Query query = new Query();

        if (StrUtil.isNotBlank(queryDTO.getType()) && !"all".equals(queryDTO.getType())) {
            query.addCriteria(Criteria.where(HttpProxy.TYPE).is(queryDTO.getType()));
        }
        if (StrUtil.isNotBlank(queryDTO.getIp()) && !"all".equals(queryDTO.getIp())) {
            query.addCriteria(Criteria.where(HttpProxy.IP).regex(".*?" + queryDTO.getIp() + ".*"));
        }
        if (StrUtil.isNotEmpty(queryDTO.getCity())) {
            query.addCriteria(Criteria.where(HttpProxy.CITY).regex(".*?" + queryDTO.getCity() + ".*"));
        }
        if (queryDTO.getScore() != null) {
            query.addCriteria(Criteria.where(HttpProxy.SCORT).is(queryDTO.getScore()));
        }
        if (!isGetAll) {
            if (StrUtil.isNotBlank(queryDTO.getSort())) {
                if ("asc".equals(queryDTO.getOrder())) {
                    query.with(new Sort(Sort.Direction.ASC, queryDTO.getSort()));
                } else {
                    query.with(new Sort(Sort.Direction.DESC, queryDTO.getSort()));
                }
            } else {
                query.with(new Sort(Sort.Direction.DESC, HttpProxy.SCORT));
                query.with(new Sort(Sort.Direction.ASC, HttpProxy.MS));
                query.with(new Sort(Sort.Direction.DESC, HttpProxy.LASTUPDATE_TIME));
            }
            int skip = (queryDTO.getPageNum() - 1) * queryDTO.getPageSize();
            query.skip(skip);
            query.limit(queryDTO.getPageSize());
        }
        return mongoTemplate.find(query, HttpProxy.class, Constant.COL_NAME_PROXY);
    }

    @Override
    public List<HttpProxy> findAll() {
        return mongoTemplate.findAll(HttpProxy.class, Constant.COL_NAME_PROXY);
    }

    @Override
    public HttpProxy getRandomByOne() {
        List<HttpProxy> httpProxies = getRandom(1);
        if (httpProxies != null && httpProxies.size() > 0) {
            return httpProxies.get(0);
        }
        return null;
    }

    @Override
    public List<HttpProxy> getRandom(Integer count) {
        List<HttpProxy> list = mongoTemplate.findAll(HttpProxy.class);
        if (list != null && list.size() > 0) {
            Collections.shuffle(list);
            int random = 0;
            if (count > 0) {
                random = RandomUtil.randomInt(0, list.size() - count);
            }
            return list.size() > count ? list.subList(random, count) : list;
        }
        return new ArrayList<>();
    }

    @Override
    public Long getTotalCount() {
        Query query = new Query();
        return mongoTemplate.count(query, HttpProxy.class);
    }

}
